/** Thread.cpp
  * implements the Thread class
  * Author: Vijay Mathew Pandyalakal
  * Date: 13-OCT-2003
**/

/* Copyright 2000 - 2005 Vijay Mathew Pandyalakal.  All rights reserved.
 *
 * This software may be used or modified for any purpose, personal or
 * commercial.  Open Source redistributions are permitted.  
 *
 * Redistributions qualify as "Open Source" under  one of the following terms:
 *   
 *    Redistributions are made at no charge beyond the reasonable cost of
 *    materials and delivery.
 *
 *    Redistributions are accompanied by a copy of the Source Code or by an
 *    irrevocable offer to provide a copy of the Source Code for up to three
 *    years at the cost of materials and delivery.  Such redistributions
 *    must allow further use, modification, and redistribution of the Source
 *    Code under substantially the same terms as this license.
 *
 * Redistributions of source code must retain the copyright notices as they
 * appear in each source code file, these license terms, and the
 * disclaimer/limitation of liability set forth as paragraph 6 below.
 *
 * Redistributions in binary form must reproduce this Copyright Notice,
 * these license terms, and the disclaimer/limitation of liability set
 * forth as paragraph 6 below, in the documentation and/or other materials
 * provided with the distribution.
 *
 * The Software is provided on an "AS IS" basis.  No warranty is
 * provided that the Software is free of defects, or fit for a
 * particular purpose.  
 *
 * Limitation of Liability. The Author shall not be liable
 * for any damages suffered by the Licensee or any third party resulting
 * from use of the Software.
 */

//-----------------------------------------------------------------
// Includes
//-----------------------------------------------------------------
#include <Tools/Thread.hpp>
#include <windows.h>


namespace Tools
{

	const int Thread::P_ABOVE_NORMAL = THREAD_PRIORITY_ABOVE_NORMAL;
	const int Thread::P_BELOW_NORMAL = THREAD_PRIORITY_BELOW_NORMAL;
	const int Thread::P_HIGHEST = THREAD_PRIORITY_HIGHEST;
	const int Thread::P_IDLE = THREAD_PRIORITY_IDLE;
	const int Thread::P_LOWEST = THREAD_PRIORITY_LOWEST;
	const int Thread::P_NORMAL = THREAD_PRIORITY_NORMAL;
	const int Thread::P_CRITICAL = THREAD_PRIORITY_TIME_CRITICAL;
	
	/**@ The Thread class implementation */
	
	/** Thread()
	  * default constructor
	**/  
	Thread::Thread() {
		m_hThread = NULL;
		m_strName = "null";
	}
	
	/** Thread(const char* nm)
	  * overloaded constructor
	  * creates a Thread object identified by "nm"
	**/  
	Thread::Thread(const char* nm) {
		m_hThread = NULL;
		m_strName = nm;
	}
	
	Thread::~Thread() {
		if(m_hThread != NULL) {
			stop();
		}
	}
	
	/** setName(const char* nm)
	  * sets the Thread object's name to "nm"
	**/  
	void Thread::setName(const char* nm) {	
		m_strName = nm;
	}
	
	/** getName()
	  * return the Thread object's name as a string
	**/  
	std::string Thread::getName() const {	
		return m_strName;
	}
	
	/** run()
	  * called by the thread callback _ou_thread_proc()
	  * to be overridden by child classes of Thread
	**/ 
	void Thread::run() {
		// Base run
	}
	
	/** sleep(long ms)
	  * holds back the thread's execution for
	  * "ms" milliseconds
	**/ 
	void Thread::sleep(long ms) {
		Sleep(ms);
	}
	
	/** start()
	  * creates a low-level thread object and calls the
	  * run() function
	**/ 
	void Thread::start() {
		DWORD tid = 0;	
		m_hThread = (unsigned long*)CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)thread_proc_callback,(Thread*)this,0,&tid);
		if(m_hThread == NULL) {
			throw ThreadException("Failed to create thread");
		}else {
			setPriority(Thread::P_NORMAL);
		}
	}
	
	/** stop()
	  * stops the running thread and frees the thread handle
	**/ 
	void Thread::stop() {
		if(m_hThread == NULL) return;	
		WaitForSingleObject(m_hThread,INFINITE);
		CloseHandle(m_hThread);
		m_hThread = NULL;
	}
	
	/** setPriority(int tp)
	  * sets the priority of the thread to "tp"
	  * "tp" must be a valid priority defined in the
	  * Thread class
	**/ 
	void Thread::setPriority(int tp) {
		if(m_hThread == NULL) {
			throw ThreadException("Thread object is null");
		}else {
			if(SetThreadPriority(m_hThread,tp) == 0) {
				throw ThreadException("Failed to set priority");
			}
		}
	}
	
	/** suspend()  
	  * suspends the thread
	**/ 
	void Thread::suspend() {
		if(m_hThread == NULL) {
			throw ThreadException("Thread object is null");
		}else {
			if(SuspendThread(m_hThread) < 0) {
				throw ThreadException("Failed to suspend thread");
			}
		}
	}
	
	/** resume()  
	  * resumes a suspended thread
	**/ 
	void Thread::resume() {
		if(m_hThread == NULL) {
			throw ThreadException("Thread object is null");
		}else {
			if(ResumeThread(m_hThread) < 0) {
				throw ThreadException("Failed to resume thread");
			}
		}
	}
	
	/** wait(const char* m,long ms)  
	  * makes the thread suspend execution until the
	  * mutex represented by "m" is released by another thread.
	  * "ms" specifies a time-out for the wait operation.
	  * "ms" defaults to 5000 milli-seconds
	**/ 
	bool Thread::wait(const char* m,long ms) {
		HANDLE h = OpenMutex(MUTEX_ALL_ACCESS,FALSE,m);
		if(h == NULL) {
			throw ThreadException("Mutex not found");
		}
		DWORD d = WaitForSingleObject(h,ms);
		switch(d) {
		case WAIT_ABANDONED:
			throw ThreadException("Mutex not signaled");
			break;
		case WAIT_OBJECT_0:
			return true;
		case WAIT_TIMEOUT:
			throw ThreadException("Wait timed out");
			break;
		}
		return false;
	}
	
	/** release(const char* m)  
	  * releases the mutex "m" and makes it 
	  * available for other threads
	**/ 
	void Thread::release(const char* m) {
		HANDLE h = OpenMutex(MUTEX_ALL_ACCESS,FALSE,m);
		if(h == NULL) {
			throw ThreadException("Invalid mutex handle");
		}
		if(ReleaseMutex(h) == 0) {
			throw ThreadException("Failed to release mutex");
		}
	}
	
	/*@ The Mutex class implementation*/
	
	
	/** Mutex()
	  * default constructor
	**/  
	Mutex::Mutex() {
		m_hMutex = NULL;
		m_strName = "";
	}
	
	/** Mutex(const char* nm)
	  * overloaded constructor
	  * creates a Mutex object identified by "nm"
	**/  
	Mutex::Mutex(const char* nm) {	
		m_strName = nm;	
		m_hMutex = (unsigned long*)CreateMutex(NULL,FALSE,nm);
		if(m_hMutex == NULL) {
			throw ThreadException("Failed to create mutex");
		}
	}
	
	/** create(const char* nm)
	  * frees the current mutex handle.
	  * creates a Mutex object identified by "nm"
	**/  
	void Mutex::create(const char* nm) {
		if(m_hMutex != NULL) {
			CloseHandle(m_hMutex);
			m_hMutex = NULL;
		}
		m_strName = nm;
		m_hMutex = (unsigned long*)CreateMutex(NULL,FALSE,nm);
		if(m_hMutex == NULL) {
			throw ThreadException("Failed to create mutex");
		}
	}
	/** getMutexHandle()
	  * returns the handle of the low-level mutex object
	**/  
	unsigned long* Mutex::getMutexHandle() {
		return m_hMutex;
	}
	
	/** getName()
	  * returns the name of the mutex
	**/ 
	std::string Mutex::getName() {
		return m_strName;
	}
	
	void Mutex::release() {
		if(m_hMutex != NULL) {
			CloseHandle(m_hMutex);
		}
	}
	
	Mutex::~Mutex() {
		/*if(m_hMutex != NULL) {
			CloseHandle(m_hMutex);
		}*/
	}
	
	// ThreadException
	ThreadException::ThreadException(const char* m) {
		msg = m;
	}
	
	std::string ThreadException::getMessage() const {
		return msg;
	}
	
	// global thread caallback
	unsigned int Thread::thread_proc_callback(void* param) {
		Thread* tp = (Thread*)param;
		tp->run();
		return 0;
	}
}
